package swagger;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.SpringVersion;

import static io.swagger.v3.oas.models.security.SecurityScheme.In.HEADER;
import static io.swagger.v3.oas.models.security.SecurityScheme.Type.*;
import static org.springdoc.core.Constants.DEFAULT_API_DOCS_URL;
import static org.springdoc.core.Constants.SPRINGDOC_ENABLED;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;

/**
 * https://editor.swagger.io
 * https://swagger.io/specification
 */
@Slf4j
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(value = SPRINGDOC_ENABLED, havingValue = "true", matchIfMissing = true)
public class SwaggerAutoConfiguration {
    /**
     * @see OpenApiCustomiser
     */
    @Bean
    OpenAPI openApi(ServiceInstance serviceInstance, @Value("${spring.application.name:application}") String name,
                    @Value("${spring.application.version:0.0.1}") String version,
                    @Value("${springdoc.swagger-ui.oauth.issuer-uri:http://auth-server:9000}") String issuerUri) {
        return new OpenAPI().components(new Components() // https://swagger.io/docs/specification/authentication
                .addSecuritySchemes("basic", new SecurityScheme().type(HTTP).scheme("basic"))
                .addSecuritySchemes("token", new SecurityScheme().type(APIKEY).name(AUTHORIZATION).in(HEADER))
                .addSecuritySchemes("bearer", new SecurityScheme().type(HTTP).scheme("bearer").bearerFormat("JWT"))
                .addSecuritySchemes("oauth2", new SecurityScheme().type(OAUTH2).flows(new OAuthFlows().authorizationCode(new OAuthFlow()
                    .tokenUrl(issuerUri.concat("/oauth2/token"))
                    .refreshUrl(issuerUri.concat("/oauth2/token"))
                    .authorizationUrl(issuerUri.concat("/oauth2/authorize"))
                ))).addSecuritySchemes("openid", new SecurityScheme().type(OPENIDCONNECT).openIdConnectUrl(issuerUri.concat("/.well-known/openid-configuration")))
            ).addSecurityItem(new SecurityRequirement().addList("basic").addList("token").addList("bearer").addList("oauth2").addList("openid"))
            .info(new Info().title(name).version(version).license(new License()
                .name("Spring Version: " + SpringVersion.getVersion())
                .url("https://spring.io/projects/spring-framework#learn")
            )).externalDocs(new ExternalDocumentation()
                .description("Spring Boot Version: ".concat(SpringBootVersion.getVersion()))
                .url("https://spring.io/projects/spring-boot#learn")
            ).addServersItem(new Server().url("/".concat(serviceInstance.getServiceId())));
    }

    @Bean
    @Profile("dev")
    ApplicationListener<ApplicationReadyEvent> swaggerUrlPrint() {
        return event -> { // https://blog.csdn.net/f641385712/article/details/88806639
            String port = event.getApplicationContext().getEnvironment().getProperty("local.server.port");
            log.info("{}：http://127.0.0.1:{}" + DEFAULT_API_DOCS_URL, "API Docs", port);
        };
    }
}